#include "Path_ex.h"

void _path_destroy_dx(path_s *pPath)
{
    if (pPath->pObj_)
    {
        pPath->pObj_->Release();
    }
    if (pPath->pGeometry_)
    {
        pPath->pGeometry_->Release();
    }
    RtlZeroMemory(pPath, sizeof(path_s));
}

BOOL _path_destroy(HEXPATH hPath)
{
    path_s *pPath = nullptr;
    INT nError = 0;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        _path_destroy_dx(pPath);
        Ex_MemFree(pPath);
        _handle_destroy(hPath, &nError);
    }
    return nError == 0;
}

BOOL _path_reset(HEXPATH hPath)
{
    path_s *pPath = nullptr;
    INT nError = 0;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        _path_destroy_dx(pPath);
        ID2D1PathGeometry *pObj = nullptr;
        nError = g_Ri.pD2Dfactory->CreatePathGeometry(&pObj);
        if (nError == 0)
        {
            pPath->pGeometry_ = pObj;
        }
    }
    return nError == 0;
}

BOOL _path_create(DWORD dwFlags, HEXPATH *hPath)
{
    INT nError = 0;
    path_s *pPath = (path_s *)Ex_MemAlloc(sizeof(path_s));
    if (pPath != 0)
    {
        *hPath = _handle_create(HT_PATH, pPath, &nError);
        if (*hPath != 0)
        {
            nError = _path_reset(*hPath) ? 0 : 1;
            pPath->dwFlags_ = dwFlags;
            if (nError != 0)
            {
                _path_destroy(*hPath);
                hPath = 0;
            }
        }
    }
    else
    {
        nError = ERROR_EX_MEMORY_ALLOC;
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_getbounds(HEXPATH hPath, RECTF *lpBounds)
{
    INT nError = 0;
    if (IsBadWritePtr(lpBounds, 16))
    {
        nError = ERROR_EX_MEMORY_BADPTR;
    }
    else
    {
        path_s *pPath = nullptr;
        if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
        {
            nError = pPath->pGeometry_->GetBounds(NULL, (D2D1_RECT_F *)lpBounds);
        }
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_open(HEXPATH hPath)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        if ((pPath->dwFlags_ & epf_bOpened) == epf_bOpened)
        {
            _path_reset(hPath);
        }
        ID2D1GeometrySink *pSink = nullptr;
        nError = pPath->pGeometry_->Open(&pSink);
        if (nError == 0)
        {
            pPath->pObj_ = pSink;
            pPath->dwFlags_ = pPath->dwFlags_ | epf_bOpened;
        }
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_close(HEXPATH hPath)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        if ((pPath->dwFlags_ & epf_bOpened) == epf_bOpened)
        {
            ID2D1GeometrySink *pSink = pPath->pObj_;
            pPath->pObj_ = NULL;
            pSink->Close();
            pSink->Release();
        }
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_beginfigure(HEXPATH hPath)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        pPath->pObj_->BeginFigure({0.5f, 0.5f}, D2D1_FIGURE_BEGIN_FILLED);
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_beginfigure3(HEXPATH hPath, FLOAT x, FLOAT y, INT figureBegin)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        pPath->pObj_->BeginFigure({x, y}, (D2D1_FIGURE_BEGIN)figureBegin);
    }
    Ex_SetLastError(nError);
    return nError;
}

BOOL _path_beginfigure2(HEXPATH hPath, FLOAT x, FLOAT y)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        pPath->pObj_->BeginFigure({x, y}, D2D1_FIGURE_BEGIN_FILLED);
    }
    Ex_SetLastError(nError);
    return nError;
}

BOOL _path_endfigure(HEXPATH hPath, BOOL fCloseFigure)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        pPath->pObj_->EndFigure(fCloseFigure == TRUE ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN);
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_hittest(HEXPATH hPath, FLOAT x, FLOAT y)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    BOOL ret = FALSE;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        LPVOID pGeometry = pPath->pGeometry_;
        ((ID2D1PathGeometry *)pGeometry)->FillContainsPoint({x, y}, NULL, 0, &ret);
    }
    Ex_SetLastError(nError);
    return ret;
}

BOOL _path_addline(HEXPATH hPath, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        if (!((pPath->dwFlags_ & EPF_DISABLESCALE) == EPF_DISABLESCALE))
        {
            if (g_Li.DpiX > 1)
            {
                x1 = x1 * g_Li.DpiX;
                y1 = y1 * g_Li.DpiX;
                x2 = x2 * g_Li.DpiX;
                y2 = y2 * g_Li.DpiX;
            }
        }
        pPath->pObj_->AddLine({x1, y1});
        pPath->pObj_->AddLine({x2, y2});
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_addarc(HEXPATH hPath, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, FLOAT radiusX, FLOAT radiusY, BOOL fClockwise)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        if (!((pPath->dwFlags_ & EPF_DISABLESCALE) == EPF_DISABLESCALE))
        {
            if (g_Li.DpiX > 1)
            {
                x1 = x1 * g_Li.DpiX;
                y1 = y1 * g_Li.DpiX;
                x2 = x2 * g_Li.DpiX;
                y2 = y2 * g_Li.DpiX;
            }
        }
        pPath->pObj_->AddLine({x1, y1});
        D2D1_ARC_SEGMENT arc = {};
        arc.point.x = x2;
        arc.point.y = y2;
        arc.size.width = radiusX;
        arc.size.height = radiusY;
        arc.sweepDirection = (fClockwise == TRUE ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE);
        pPath->pObj_->AddArc(arc);
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_addarc3(HEXPATH hPath, FLOAT x, FLOAT y, FLOAT radiusX, FLOAT radiusY, FLOAT startAngle, FLOAT sweepAngle, BOOL fClockwise, BOOL barcSize)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        if (!((pPath->dwFlags_ & EPF_DISABLESCALE) == EPF_DISABLESCALE))
        {
            if (g_Li.DpiX > 1)
            {
                x = x * g_Li.DpiX;
                y = y * g_Li.DpiX;
            }
        }
        D2D1_ARC_SEGMENT arc = {};
        arc.point.x = x;
        arc.point.y = y;
        arc.size.width = radiusX;
        arc.size.height = radiusY;
        arc.rotationAngle = sweepAngle - startAngle;
        arc.sweepDirection = (fClockwise == TRUE ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE);
        arc.arcSize = (barcSize == TRUE ? D2D1_ARC_SIZE_LARGE : D2D1_ARC_SIZE_SMALL);
        pPath->pObj_->AddArc(arc);
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_addarc2(HEXPATH hPath, FLOAT x, FLOAT y, FLOAT width, FLOAT height, FLOAT startAngle, FLOAT sweepAngle)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        if (!((pPath->dwFlags_ & EPF_DISABLESCALE) == EPF_DISABLESCALE))
        {
            if (g_Li.DpiX > 1)
            {
                x = x * g_Li.DpiX;
                y = y * g_Li.DpiX;
            }
        }

        ID2D1GeometrySink *s = pPath->pObj_;
        D2D1_ARC_SEGMENT arc = {};
        FLOAT rx = width / 2;                // x半径
        FLOAT ry = height / 2;               // y半径
        FLOAT theta = NULL;                  // 夹角θ
        D2D1_POINT_2F ptOrg{x + rx, y + ry}; // 坐标系原点
        D2D1_POINT_2F pPoints{};             // 椭圆顶点XY序列
        FLOAT pi = 3.141592654f;

        // 求与x正方向夹角θ
        theta = startAngle;
        // 求点坐标
        pPoints.x = cos(theta * pi / 180) * rx + ptOrg.x;
        pPoints.y = sin(theta * pi / 180) * ry + ptOrg.y;
        s->AddLine({pPoints.x, pPoints.y});

        theta = sweepAngle;
        // 求点坐标
        pPoints.x = cos(theta * pi / 180) * rx + ptOrg.x;
        pPoints.y = sin(theta * pi / 180) * ry + ptOrg.y;
        arc.point.x = pPoints.x;
        arc.point.y = pPoints.y;
        arc.size.width = rx;
        arc.size.height = ry;
        arc.rotationAngle = 0.0F;
        arc.sweepDirection = D2D1_SWEEP_DIRECTION::D2D1_SWEEP_DIRECTION_CLOCKWISE;
        arc.arcSize = (((sweepAngle - startAngle) > 180) == TRUE ? D2D1_ARC_SIZE_LARGE : D2D1_ARC_SIZE_SMALL);
        s->AddArc(&arc);
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_addrect(HEXPATH hPath, FLOAT left, FLOAT top, FLOAT right, FLOAT bottom)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        if ((pPath->dwFlags_ & EPF_DISABLESCALE) != EPF_DISABLESCALE)
        {
            if (g_Li.DpiX > 1)
            {
                left = left * g_Li.DpiX;
                top = top * g_Li.DpiX;
                right = right * g_Li.DpiX;
                bottom = bottom * g_Li.DpiX;
            }
        }
        ID2D1GeometrySink *pSink = pPath->pObj_;
        left += 0.5f;
        top += 0.5f;
        right += 0.5f;
        bottom += 0.5f;
        pSink->AddLine({left, top});
        pSink->AddLine({right, top});
        pSink->AddLine({right, bottom});
        pSink->AddLine({left, bottom});
        pSink->AddLine({left, top});
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_addroundedrect(HEXPATH hPath, FLOAT left, FLOAT top, FLOAT right, FLOAT bottom, FLOAT radiusTopLeft, FLOAT radiusTopRight, FLOAT radiusBottomLeft, FLOAT radiusBottomRight)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        LPVOID pObj = pPath->pObj_;
        if (radiusTopLeft == 0) //左上->右上
        {
            _path_addline(hPath, left, top, right - radiusTopRight, top);
        }
        else
        {
            _path_addarc(hPath, left, top + radiusTopLeft, left + radiusTopLeft, top, radiusTopLeft, radiusTopLeft, TRUE);
        }

        if (radiusTopRight == 0) //右上-右下
        {
            _path_addline(hPath, right, top, right, bottom - radiusBottomRight);
        }
        else
        {
            _path_addarc(hPath, right - radiusTopRight, top, right, top + radiusTopRight, radiusTopRight, radiusTopRight, TRUE);
        }

        if (radiusBottomRight == 0) //右下-左下
        {
            _path_addline(hPath, right, bottom, left + radiusBottomLeft, bottom);
        }
        else
        {
            _path_addarc(hPath, right, bottom - radiusBottomRight, right - radiusBottomRight, bottom, radiusBottomRight, radiusBottomRight, TRUE);
        }

        if (radiusBottomLeft == 0) //左下-左上
        {
            _path_addline(hPath, left, bottom, left, top + radiusTopLeft);
        }
        else
        {
            _path_addarc(hPath, left + radiusBottomLeft, bottom, left, bottom - radiusBottomLeft, radiusBottomLeft, radiusBottomLeft, TRUE);
            _path_addline(hPath, left, bottom - radiusBottomLeft, left, top + radiusTopLeft);
        }
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_addbezier(HEXPATH hPath, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, FLOAT x3, FLOAT y3)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        if ((pPath->dwFlags_ & EPF_DISABLESCALE) != EPF_DISABLESCALE)
        {
            if (g_Li.DpiX > 1)
            {
                x1 = x1 * g_Li.DpiX;
                y1 = y1 * g_Li.DpiX;
                x2 = x2 * g_Li.DpiX;
                y2 = y2 * g_Li.DpiX;
                x3 = x3 * g_Li.DpiX;
                y3 = y3 * g_Li.DpiX;
            }
        }
        D2D1_BEZIER_SEGMENT bs = {};
        bs.point1.x = x1;
        bs.point1.y = y1;
        bs.point2.x = x2;
        bs.point2.y = y2;
        bs.point3.x = x3;
        bs.point3.y = y3;
        pPath->pObj_->AddBezier(bs);
    }
    Ex_SetLastError(nError);
    return nError == 0;
}

BOOL _path_addquadraticbezier(HEXPATH hPath, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2)
{
    INT nError = 0;
    path_s *pPath = nullptr;
    if (_handle_validate(hPath, HT_PATH, (LPVOID *)&pPath, &nError))
    {
        if ((pPath->dwFlags_ & EPF_DISABLESCALE) != EPF_DISABLESCALE)
        {
            if (g_Li.DpiX > 1)
            {
                x1 = x1 * g_Li.DpiX;
                y1 = y1 * g_Li.DpiX;
                x2 = x2 * g_Li.DpiX;
                y2 = y2 * g_Li.DpiX;
            }
        }
        D2D1_QUADRATIC_BEZIER_SEGMENT bs = {};
        bs.point1.x = x1;
        bs.point1.y = y1;
        bs.point2.x = x2;
        bs.point2.y = y2;
        pPath->pObj_->AddQuadraticBezier(bs);
    }
    Ex_SetLastError(nError);
    return nError == 0;
}